package cn.thr.ymcc.service.impl;

import cn.thr.ymcc.domain.CourseType;
import cn.thr.ymcc.mapper.CourseTypeMapper;
import cn.thr.ymcc.service.ICourseTypeService;
import cn.thr.ymcc.util.AssertUtil;
import cn.thr.ymcc.vo.CourseTypeCrumbsVo;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.checkerframework.checker.units.qual.A;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * <p>
 * 课程目录 服务实现类
 * </p>
 *
 * @author thr
 * @since 2023-02-28
 */
@Service
public class CourseTypeServiceImpl extends ServiceImpl<CourseTypeMapper, CourseType> implements ICourseTypeService {
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    private static  final  String COURSE_TYPE_TREE_DATA = "course_type_tree_data";


    /**
     * 查询课程类别树
     * @param
     * @return 返回最顶层类别的集合，所有的非顶级类别都装在他父亲的儿子集合中
     *
     * 要使用SpringCache 首先需要在启动类喝着配置上添加@EnableCaching 注释开启
     * 然后再查询方法上添加@Cacheable (自己封装的一个常量，key 是可以变换的)
     * cacheNames ：指定一个缓存名称
     * key: 指定一个缓存key
     *
     * springcache 会将cacheNames 和 key 拼接成 course_type_tree_data::ALL
     * 注意：key 属性是个表达式，如果要写字符串，必须添加单引号
     */
    @Cacheable(cacheNames = COURSE_TYPE_TREE_DATA,key = "'ALL'")
    @Override
    public List<CourseType> treeData() {
        //查询数据库之前看缓存里面有没得数据
//       if(redisTemplate.hasKey(COURSE_TYPE_TREE_DATA)){
//       return  (List<CourseType>) redisTemplate.opsForValue().get(COURSE_TYPE_TREE_DATA);
//       }
//
//       //缓存中没有数据，就到数据库里面查询，再缓存一份  forMap()这个方法中有数据
//        List<CourseType> courseTypes =forMap();
//        redisTemplate.opsForValue().set(COURSE_TYPE_TREE_DATA,courseTypes);
        return  forMap();
    }

    /**
     * 加载面包血接口
     * 1.先通过传入的courseTypeId查询一行数据
     * 2.获取它的path 字段
     * 3.path字段值以拆分字符串，得到数组
     * 4.循环
     *      查询本次循环的课程类别id对应的一行数据
     *      查询本次循环的课程类别id对应的兄弟集合
     *      封装courseTypeCrumbsVo对象放入list集合中
     * @param courseTypeId
     * @return
     */
    @Override
    public List<CourseTypeCrumbsVo> loadCrumbs(Long courseTypeId) {

        List<CourseTypeCrumbsVo> voList = new ArrayList<>();

        //1.先通过传入的courseTypeId查询一行数据
        CourseType courseType = baseMapper.selectById(courseTypeId);

        //2.获取它的path 字段
        String path = courseType.getPath();

        //3.path字段值以拆分字符串，得到数组
        String[] split = path.split("\\.");

        //4.循环
        for (String s : split) {
            //查询本次循环的课程类别id对应的一行数据
            CourseType courrent = baseMapper.selectById(s);

            //查询本次循环的课程类别id对应的兄弟集合 ,他的兄弟的pid相同
            List<CourseType> brothers = baseMapper.selectList(new QueryWrapper<CourseType>().eq("pid", courrent.getPid()));

            //封装courseTypeCrumbsVo对象放入list集合中
            voList.add(new CourseTypeCrumbsVo(courrent,brothers));
        }


        return voList;
    }

    /**
     * 查询课程树类别-for循环——hashMap
     */
    public List<CourseType> forMap(){
        //顶级课程类别集合 把对象变成list集合
        List<CourseType> firstLevels = new ArrayList<>();

        //一次性把所有的课程类别查出来，降低数据库压力  查询全部selectList(null); 不带条件
        List<CourseType> courseTypes = baseMapper.selectList(null);

        //先将所有的课程类别分装到一个HashMap中；以课程的id为key 以课程类别对象为值
        Map<Long,CourseType> allCourseTypeMap = new HashMap<>();
        //将所有的课程类别进行循环 以courseTypes id 为键 courseTypes 对象为值 存入HashMap 集合中
        courseTypes.forEach(courseType -> allCourseTypeMap.put(courseType.getId(),courseType));

      /*  Map<Long,CourseType> allCourseTypeMap = courseTypes.stream()
                .collect(Collectors.toMap(courseType -> courseType.getId(),courseType -> courseType));*/
        //组装树
        CourseType paren = null;
        for (CourseType courseType : courseTypes){
            //判断当前循环是不是顶级，如果是就添加到顶级去
            if(courseType.getPid() ==0){
                firstLevels.add(courseType);
            }else {
                //如果不是顶级，就去查他的爹，并把它设置成他爹的儿子
                //这里paren 就有courseType 的pid了
              paren =  allCourseTypeMap.get(courseType.getPid());
              paren.getChildren().add(courseType);
            }
        }
        return firstLevels;
    }
    @CacheEvict(cacheNames = COURSE_TYPE_TREE_DATA,key = "'ALL'")
    @Transactional
    @Override
    public boolean save(CourseType entity) {
        super.save(entity);
        //维护path 字段 等于它付清path 他自己的id
        CourseType parent = baseMapper.selectById(entity.getPid());
        entity.setPath(parent.getPath()+"."+entity.getId());
        super.updateById(entity);
        return true;
    }
    @CacheEvict(cacheNames = COURSE_TYPE_TREE_DATA,key = "'ALL'")
    @Transactional
    @Override
    public boolean removeById(Serializable id) {
        List<CourseType> courseType = baseMapper.selectList(new QueryWrapper<CourseType>().eq("pia", id));
        AssertUtil.isTrue(courseType == null || courseType.size() == 0 ,"下面还有子类别不能删除" );
        return super.removeById(id);
    }
    @CacheEvict(cacheNames = COURSE_TYPE_TREE_DATA,key = "'ALL'")
    @Transactional
    @Override
    public boolean updateById(CourseType entity) {
        return super.updateById(entity);
    }
}
